/**
 * HttpClientUtil.java<br>
 *
 * @author aaronwong.sd@foxmail.com
 * @version 下午3:23:55
 */
package com.iwechat.httpclient;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.FileEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.util.EntityUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.alibaba.fastjson.JSONObject;
import com.iwechat.util.PropertyUtils;

/**
 * http工具类
 *
 * @author aaronwong.sd@foxmail.com
 */
public class HttpClientUtil implements IHttpUtil {
    /** logger */
    private static final Logger         logger   = LogManager.getLogger();
    private static final HttpClientUtil instance = new HttpClientUtil();
    private HttpClient                  client   = HttpClient.getInstance();
    private PropertyUtils               config   = PropertyUtils.getInstance();

    private String                      charset;

    public static HttpClientUtil getInstance() {
        return instance;
    }

    private HttpClientUtil() {
        charset = config.getStringValue("httpclient", "charset");
        logger.info("### init succeed!");
    }

    /*
     * (non-Javadoc)
     *
     * @see com.wctm.api.http.IHttpUtil#sendPost(java.lang.String, java.lang.String, java.lang.String)
     */
    @Override
    public Response sendPost(String url, String data, String sequence) throws Exception {
        String retStr = "{}";
        Response respObj = new Response();
        long begin = System.currentTimeMillis();
        CloseableHttpResponse response = null;
        try {
            StringBuffer logbuf = new StringBuffer();
            logbuf.append(sequence == null ? "" : sequence);
            logbuf.append(" send_post_to : ");
            logbuf.append(url);
            logbuf.append("; ");
            logbuf.append("send_data : ");
            logbuf.append(data);
            logger.info(logbuf.toString());

            HttpPost httpPost = new HttpPost(url);
            HttpEntity entity = new StringEntity(data, charset);
            httpPost.setEntity(entity);
            response = client.execute(httpPost);
            StatusLine statusLine = response.getStatusLine();
            respObj.setStatus(statusLine.getStatusCode());
            if (HttpStatus.SC_OK == statusLine.getStatusCode()) {
                HttpEntity re = response.getEntity();
                retStr = EntityUtils.toString(re, charset);
                retStr = retStr == null ? "" : retStr.trim();
            } else {
                JSONObject respJson = this.getJsonerrcode(statusLine);
                respJson.put("sequence", sequence == null ? "" : sequence);
                retStr = respJson.toJSONString();
            }
            respObj.setDataStr(retStr);
            logbuf = new StringBuffer();
            logbuf.append(sequence == null ? "" : sequence);
            logbuf.append(" response_status : ");
            logbuf.append(statusLine.getStatusCode());
            logbuf.append("; ");
            logbuf.append("consuming_time : ");
            logbuf.append(System.currentTimeMillis() - begin);
            logbuf.append(" ms; ");
            logbuf.append("recive_data : ");
            logbuf.append(retStr);
            logger.info(logbuf.toString());
        } catch (IOException e) {
            throw new HttpException("HttpClientUtil.sendPost() throw IOException", e);
        } catch (Exception e) {
            logger.error("收发HTTP请求时异常！", e);
        } finally {
            try {
                if (response != null) {
                    EntityUtils.consume(response.getEntity());
                }
            } catch (IOException e) {
            }
        }

        return respObj;
    }

    /*
     * (non-Javadoc)
     *
     * @see com.wctm.api.http.IHttpUtil#sendGet(java.lang.String, java.util.Map, java.lang.String)
     */
    @Override
    public Response sendGet(String url, Map<String, String> urlParams, String sequence) throws Exception {
        long begin = System.currentTimeMillis();
        URI uri = null;
        String retStr = "";
        Response respObj = new Response();
        CloseableHttpResponse response = null;
        try {
            URIBuilder builder = new URIBuilder(url);
            if (urlParams != null) {
                for (Entry<String, String> e : urlParams.entrySet()) {
                    builder = builder.addParameter(URLEncoder.encode(e.getKey(), this.charset), URLEncoder.encode(e.getValue(), this.charset));
                }
            }
            uri = builder.build();
            StringBuffer logbuf = new StringBuffer();
            logbuf.append(sequence == null ? "" : sequence);
            logbuf.append(" send_get_to : ");
            logbuf.append(uri.toString());
            logger.info(logbuf.toString());

            HttpGet httpGet = new HttpGet(url);
            response = client.execute(httpGet);
            StatusLine statusLine = response.getStatusLine();
            respObj.setStatus(statusLine.getStatusCode());
            if (HttpStatus.SC_OK == statusLine.getStatusCode()) {
                HttpEntity entity = response.getEntity();
                retStr = EntityUtils.toString(entity, charset);
                retStr = retStr == null ? "" : retStr.trim();
            } else {
                JSONObject respJson = this.getJsonerrcode(statusLine);
                respJson.put("sequence", sequence == null ? "" : sequence);
                retStr = respJson.toJSONString();
            }
            respObj.setDataStr(retStr);
            logbuf = new StringBuffer();
            logbuf.append(sequence == null ? "" : sequence);
            logbuf.append(" response_status : ");
            logbuf.append(statusLine.getStatusCode());
            logbuf.append("; ");
            logbuf.append("consuming_time : ");
            logbuf.append(System.currentTimeMillis() - begin);
            logbuf.append(" ms; ");
            logbuf.append("recive_data : ");
            logbuf.append(retStr);
            logger.info(logbuf.toString());
        } catch (IOException e) {
            throw new HttpException("HttpClientUtil.sendGet() throw IOException", e);
        } catch (Exception e) {
            logger.error("收发HTTP请求时异常！", e);
        } finally {
            try {
                if (response != null) {
                    EntityUtils.consume(response.getEntity());
                }
            } catch (IOException e) {
            }
        }
        return respObj;
    }

    /**
     * 获得{"errorCode":"status"}样式的错误码
     *
     * @param statusLine
     * @return 返回json字符串
     */
    public JSONObject getJsonerrcode(StatusLine statusLine) {
        JSONObject ret = new JSONObject();
        ret.put("errorCode", statusLine.getStatusCode());
        ret.put("errorMesg", statusLine.getReasonPhrase());
        return ret;
    }

    /*
     * (non-Javadoc)
     *
     * @see com.iwechat.httpclient.IHttpUtil#upload(java.lang.String, java.io.File)
     */
    @Override
    public String upload(String url, File file) throws HttpException {
        String retStr;
        HttpPost httppost = new HttpPost(url);
        if (file != null && file.exists()) {
            FileEntity bin = new FileEntity(file);
            httppost.setEntity(bin);
        }

        logger.info("executing request " + httppost.getRequestLine());
        CloseableHttpResponse response = null;
        try {
            response = client.execute(httppost);
            StatusLine statusLine = response.getStatusLine();
            if (HttpStatus.SC_OK == statusLine.getStatusCode()) {
                HttpEntity entity = response.getEntity();
                retStr = EntityUtils.toString(entity, charset);
                retStr = retStr == null ? "" : retStr.trim();
            } else {
                JSONObject respJson = this.getJsonerrcode(statusLine);
                retStr = respJson.toJSONString();
            }
        } catch (IOException e) {
            throw new HttpException("HttpClientUtil.upload() throw IOException", e);
        } catch (Exception e) {
            logger.error("收发HTTP请求时异常！", e);
        } finally {
            try {
                if (response != null) {
                    EntityUtils.consume(response.getEntity());
                }
            } catch (IOException e) {
            }
        }
        return null;
    }

    /*
     * (non-Javadoc)
     *
     * @see com.iwechat.httpclient.IHttpUtil#download(java.lang.String)
     */
    @Override
    public Attachment download(String url) throws HttpException {
        Attachment att = new Attachment();
        String retStr = "";
        CloseableHttpResponse response = null;
        try {
            HttpGet httpGet = new HttpGet(url);
            response = client.execute(httpGet);
            StatusLine statusLine = response.getStatusLine();
            if (HttpStatus.SC_OK == statusLine.getStatusCode()) {
                HttpEntity entity = response.getEntity();
                if (entity instanceof StringEntity) {
                    retStr = EntityUtils.toString(entity, charset);
                    retStr = retStr == null ? "" : retStr.trim();
                    att.setError(retStr);
                } else if (entity.isStreaming()) {
                    String fullName = getFileName(response);
                    String relName;
                    String suffix;
                    int index = fullName.lastIndexOf(".");
                    if (index > 0) {
                        relName = fullName.substring(0, index);
                        suffix = fullName.substring(relName.length() + 1);
                    } else {
                        relName = fullName;
                        suffix = "";
                    }
                    att.setContentLength(entity.getContentLength());
                    att.setContentType(entity.getContentType().getValue());
                    att.setFileStream(new BufferedInputStream(entity.getContent()));
                    att.setFullName(fullName);
                    att.setFileName(relName);
                    att.setSuffix(suffix);
                }
            } else {
                JSONObject respJson = this.getJsonerrcode(statusLine);
                retStr = respJson.toJSONString();
                att.setError(retStr);
            }
        } catch (IOException e) {
            throw new HttpException("HttpClientUtil.download() throw IOException", e);
        } catch (Exception e) {
            logger.error("收发HTTP请求时异常！", e);
        } finally {
            try {
                if (response != null) {
                    EntityUtils.consume(response.getEntity());
                }
            } catch (IOException e) {
            }
        }
        return att;
    }

    /**
     * 获取response header中Content-Disposition中的filename值
     *
     * @param response
     * @return
     */
    private String getFileName(HttpResponse response) {
        Header contentHeader = response.getFirstHeader("Content-disposition");
        String filename = null;
        if (contentHeader != null) {
            HeaderElement[] values = contentHeader.getElements();
            if (values.length == 1) {
                NameValuePair param = values[0].getParameterByName("filename");
                if (param != null) {
                    filename = param.getValue();
                }
            }
        } else {
            filename = String.valueOf(System.currentTimeMillis());
        }
        return filename;
    }
}
